/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "NodeCpp/Data/Rel/SqlOperation.h"
#include <cstdarg>
#include "NodeCpp/String.h"
#include "NodeCpp/Data/Rel/SqlConnection.h"
#include "NodeCpp/Data/Rel/SqlPreparedStatement.h"
#include "NodeCpp/Data/Rel/SqlDelayThread.h"
#include "NodeCpp/Data/Rel/Session.h"
#include "NodeCpp/Data/Rel/QueryResult.h"

namespace NodeCpp {
namespace Data {
namespace Rel {

    SqlPreparedRequest::SqlPreparedRequest(int _Index, SqlStmtParameters* _Arg)
        : Index_(_Index), Param_(_Arg)
    {
    }

    SqlPreparedRequest::~SqlPreparedRequest()
    {

    }

    bool SqlPreparedRequest::Execute(SqlConnection* _Conn)
    {
        SqlConnection::Lock _Guard(_Conn);
        return _Conn->ExecuteStmt(Index_, *Param_);
    }

    void SqlResultQueue::Update(void)
    {
        IQueryCallback* _Callback = NULL;
        while (Next(_Callback))
        {
            AutoPtr<IQueryCallback> _CBPtr(_Callback);
            _CBPtr->Execute();
        }
    }

    bool SqlQuery::Execute(SqlConnection* _Conn)
    {
        if (!Callback_ || !Queue_) {
            return false;
        }

        SqlConnection::Lock _Guard(_Conn);
        Callback_->SetResult(_Conn->Query(Sql_));
        Queue_->Add(Callback_);
        return true;
    }

    bool SqlQueryHolder::Execute(IQueryCallback* _Callback, SqlDelayThread* _Thread, SqlResultQueue* _Queue)
    {
        if (!_Callback || !_Thread || !_Queue)
        { return false; }

        /// delay the execution of the queries, sync them with the delay thread
        /// which will in turn resync on execution (via the queue) and call back
        SqlQueryHolderEx* _HolderEx = new SqlQueryHolderEx(this, _Callback, _Queue);
        _Thread->Delay(_HolderEx);
        return true;
    }

    bool SqlQueryHolder::SetQuery(std::size_t _Index, const char* _Sql)
    {
        if (Queries_.size() <= _Index) {
            return false;
        }

        if (!Queries_[_Index].first.empty()) {
            return false;
        }

        /// not executed yet, just stored (it's not called a holder for nothing)
        Queries_[_Index] = SqlResultPair(std::string(_Sql), (QueryResult*)NULL);
        return true;
    }

    bool SqlQueryHolder::SetPQuery(std::size_t _Index, const char* _Format, ...)
    {
        if (!_Format) {
            return false;
        }

        va_list _Ap;
        char _SzQuery [Session::MAX_QUERY_LENGTH];
        va_start(_Ap, _Format);
        int _Res = NodeCpp::vsnprintf(_SzQuery, Session::MAX_QUERY_LENGTH, _Format, _Ap);
        va_end(_Ap);

        if (_Res == -1) {
            return false;
        }

        return SetQuery(_Index, _SzQuery);
    }

    QueryResult* SqlQueryHolder::GetResult(std::size_t _Index)
    {
        if (_Index < Queries_.size()) {
            return Queries_[_Index].second;
        }
        else
        { return NULL; }
    }

    void SqlQueryHolder::SetResult(std::size_t _Index, QueryResult* _Result)
    {
        /// store the result in the holder
        if (_Index < Queries_.size())
        { Queries_[_Index].second = _Result; }
    }

    SqlQueryHolder::~SqlQueryHolder()
    {
        for (std::size_t _I = 0; _I < Queries_.size(); ++_I)
        {
            /// if the result was never used, free the resources
            /// results used already (getresult called) are expected to be deleted
            SAFE_DEL(Queries_[_I].second);
        }
    }

    void SqlQueryHolder::SetSize(std::size_t _Size)
    {
        /// to optimize push_back, reserve the number of queries about to be executed
        Queries_.resize(_Size);
    }

    bool SqlQueryHolderEx::Execute(SqlConnection* _Conn)
    {
        if (!Holder_ || !Callback_ || !Queue_)
        { return false; }

        SqlConnection::Lock _Guard(_Conn);
        /// we can do this, we are friends
        std::vector<SqlQueryHolder::SqlResultPair>& _Queries = Holder_->Queries_;
        for (std::size_t _I = 0; _I < _Queries.size(); ++_I)
        {
            /// execute all queries in the holder and pass the results
            char const* _Sql = _Queries[_I].first.c_str();
            if (_Sql) { Holder_->SetResult(_I, _Conn->Query(_Sql)); }
        }

        /// sync with the caller thread
        Queue_->Add(Callback_);

        return true;
    }

}}}